﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using PMC.Interfaces;
using PMC.Structs;

namespace PMC.Extensions.Default
{
    public class PTCExtension : IExtension
    {
        private PtcExtensionControl control;
        public BindingList<PTCWebsite> inputUrls = new BindingList<PTCWebsite>();
        public BindingList<string> resultUrls = new BindingList<string>();
        private string settingsFile = Properties.Settings.Default.SettingsFolder + @"\\PTCExtension.xml";

        //public Uri CurrentUrl { get; private set; }

        public TabPage SettingsControl
        {
            get { return control ?? (control = new PtcExtensionControl(this)); }
        }

        public PTCExtension()
        {
            inputUrls.ListChanged += inputUrls_ListChanged;
            Website.WebsiteDeleted += Website_WebsiteDeleted;
        }

        void Website_WebsiteDeleted(Website.WebsiteEventArgs args)
        {
            inputUrls.Remove(inputUrls.FirstOrDefault(c => c.Website == args.Website));
        }

        void inputUrls_ListChanged(object sender, ListChangedEventArgs e)
        {
            changed = true;
        }

        public void Process()
        {
            if (OnActivated())
                return;

            INotifyProgress progress = new ProgressForm();
            Thread t = new Thread(c=>Process(progress)) { IsBackground = true, };
            t.SetApartmentState(ApartmentState.STA);
            t.Start();
        }

        private void Process(INotifyProgress progressForm)
        {
            double currentWebsite = 0;
            foreach (PTCWebsite website in inputUrls)
            {
                var path = website.Url;
                website.Website.Login(website.Url.PathAndQuery); //BUG may contain bug, with login

                Uri startpost = path;
                do
                {
                    bool nextPossible = false;
                    var document = Global.HtmlUtility.HttpPageRequest(startpost);

                    if (document != null)
                    {
                        //Some websites may have sub_ptc iframe with the ptc's in it, lets extract them
                        foreach (var element in document.DocumentNode.Descendants("iframe"))
                        {
                            if (element.GetAttributeValue("src", string.Empty).IndexOf("sub_ptc.php", StringComparison.OrdinalIgnoreCase) == -1)
                                continue;
                            path = new Uri(path, "sub_ptc.php");
                            startpost = path;
                            document = Global.HtmlUtility.HttpPageRequest(path);
                            break;
                        }

                        foreach (var link in document.DocumentNode.Descendants("a"))
                        {
                            string attribute = link.GetAttributeValue("href", string.Empty);

                            if (attribute.IndexOf("startpos=", StringComparison.OrdinalIgnoreCase) > -1)
                            {
                                var query = UrlToQueryArray(attribute);
                                var newuri = new Uri(path, "?startpos=" + query["startpos"]);

                                var lastquery = UrlToQueryArray(startpost.ToString());

                                if (!lastquery.ContainsKey("startpos") ||
                                    int.Parse(query["startpos"]) > int.Parse(lastquery["startpos"]))
                                {
                                    startpost = newuri;
                                    nextPossible = true;
                                }
                            }

                            string target = link.GetAttributeValue("target", string.Empty);

                            if (!target.Equals("_ptc", StringComparison.OrdinalIgnoreCase))
                                continue;

                            if (!Uri.IsWellFormedUriString(attribute, UriKind.Absolute))
                                continue;
                            resultUrls.Add(attribute);
                        }
                    }

                    if (!nextPossible)
                            startpost = null;
                } while (startpost != null);

                //Stats
                currentWebsite++;
                var number = (int)((currentWebsite / inputUrls.Count) * 100);
                progressForm.NotifyProgress(number);
            }
            progressForm.NotifyProgress(100);
            if (resultUrls.Count > 0)
            {
                var result = MessageBox.Show(resultUrls.Count + " links found, would you like to save them in order to read them?", "Save?", MessageBoxButtons.YesNo);
                if (result == DialogResult.Yes)
                    SaveUrls();
            }
            resultUrls.Clear();
            OnFinished();
        }

        private void SaveUrls()
        {
            var dialog = new SaveFileDialog();
            dialog.Filter = "Text files (.txt) | *.txt";
            var result = dialog.ShowDialog();

            if (result == DialogResult.OK)
            {
                StreamWriter txt = null;
                try
                {
                    txt = new StreamWriter(dialog.FileName);
                    foreach (string url in resultUrls)
                    {
                        txt.WriteLine(url);
                    }
                }
                catch (Exception exception)
                {
                    Log.Instance.AddException(exception);
                    throw;
                }
                finally
                {
                    if (txt != null)
                        txt.Close();
                }
            }
        }

        private Dictionary<string, string> UrlToQueryArray(string url)
        {
            if (!url.Contains("?"))
                return new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);

            var query = url.Substring(url.IndexOf("?") + 1);
            var splitted = query.Split(new[] { '=' });
            if (splitted.Length % 2 != 0)
                throw new FormatException();

            var returnarray = new Dictionary<string, string>();
            for (int i = 0; i < splitted.Length; i+=2)
            {
                returnarray.Add(splitted[i], splitted[i + 1]);
            }
            return returnarray;
        }

        public event EventHandler Finished;
        public event CancelEventHandler Activated;

        public void OnFinished()
        {
            if (Finished == null)
                return;

            Finished(this, EventArgs.Empty);
        }

        /// <summary>
        /// Notifies that extension has been activated
        /// </summary>
        /// <returns><b>Boolean</b> representing if the activation would like to be canceled</returns>
        public bool OnActivated()
        {
            if (Activated == null)
                return false;

            var cancel = new CancelEventArgs();
            Activated(this, cancel);
            return cancel.Cancel;
        }

        private bool changed;
        public bool Changed
        {
            get { return changed; }
            set { changed = value; }
        }

        public void Load()
        {
            try
            {
                var deserialize = Global.XMLDeserialize<BindingList<PTCWebsite>>(settingsFile);

                if (deserialize == null)
                    return;

                foreach (var uri in deserialize)
                {
                    // A website may not exist anymore, in that case the website property will be null.
                    if (uri.Website != null)
                        inputUrls.Add(uri);
                }
                changed = false;
            }
            catch (Exception exception)
            {
                Log.Instance.AddException(exception);
                //throw;
            }
        }

        public void Save()
        {
            try
            {
                Global.XMLSerialize(settingsFile, inputUrls);
                changed = false;
            }
            catch (Exception e) //Todo if I put this in seperate project I will need to catch this myself and notify!
            {
                Log.Instance.AddException(e);
                throw;
            }
        }

        public void Reload()
        {
            Load();
        }
    }

    [DataContract]
    public class PTCWebsite : INotifyPropertyChanged
    {
        private Website _website;
        public Website Website
        {
            get { return _website; }
            set { _website = value; OnPropertyChanged("Website"); }
        }

        private Uri _url;

        [DataMember]
        public Uri Url
        {
            get { return _url; }
            set { _url = value; OnPropertyChanged("Url");}
        }

        [DataMember]
        private string XMLWebsite
        {
            get { return Website.Name; }
            set { Website = Website.Websites.FirstOrDefault(website => website.Name == value); }
        }

        public PTCWebsite()
        {
        }

        public PTCWebsite(Website website, Uri url)
        {
            Website = website;
            Url = url;
        }

        public static implicit operator Uri(PTCWebsite website)
        {
            return website.Url;
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyChanged(string property)
        {
            if (PropertyChanged != null)
            PropertyChanged.Invoke(this, new PropertyChangedEventArgs(property));
        }
    }
}
